/**
 * @class UI-基础件-左侧栏
 * @author DuskyHuang 圣子
 * @description 包含显示控制、比例缩放、介绍按钮等左侧组件
*/

import { _decorator, Node, Toggle, Sprite, Slider, Label, BlockInputEvents } from 'cc';
import { core, Mediator, TweenSwitcher, utils } from 'cfw';
import { LBL_GRAY, LBL_UI_NAMES, Res } from 'const';
import { View, UI } from 'notifications';
import { VisitProxy, cosmos, planetary, solar, ui, visit } from 'proxies';
import { LabelI18n } from '../label-i18n';
const { group, findComp } = utils;
const { ccclass } = _decorator;

@ccclass('cocosmos.left-menu')
export class LeftMenu extends Mediator {

    @group('Scale', '', Node) public scaleGroup: Node = null;
    @group('Scale', '', Slider) public scalar: Slider = null;
    @group('Scale', '', Node) public visibleList: Node = null;
    @group('Scale', '', BlockInputEvents) public blocker: BlockInputEvents = null;

    @group('Intro', '', Node) public introGroup: Node = null;
    @group('Intro', '', LabelI18n) public introName: LabelI18n = null;

    protected onLoad(): void {
        this._init();
        this._listen();
    }

    protected start(): void {
        this.scaleGroup.active = this.introGroup.active = false;
        this._initToggleStates();
    }

    protected update(dt: time.sec): void {
        if (this._reducing) {
            this.scalar.progress -= dt / REDUCING_SEC;
            if (this.scalar.progress <= 0) {
                this.scalar.progress = 0;
                this._reducing = false;
            }
            this.notify(Solar.RATIO, this.scalar.progress);
        }
        if (!visit.transforming) {
            this.scaleGroup.active = cosmos.underSolar && visit.inSolar;
        }
        if (this._should_intro && isNum(visit.center?.suggest)) {
            this.introGroup.active = (visit.center?.suggest + VisitProxy.DETAIL_TOLERANCE) > cosmos.pow10;
        }
    }

    private _init(): void {
        this._scaleBar = findComp(Sprite, this.scalar, 'Bar');
        this._twsScale = this.scaleGroup.getComponent(TweenSwitcher);
        this._twsIntro = this.introGroup.getComponent(TweenSwitcher);
        this._prefix = core.res.getConfigItem<i18nConfig>(Res.Configs.I18N, 'diameter') ?? {};
        this._actual = core.res.getConfigItem<i18nConfig>(Res.Configs.I18N, 'actual') ?? {};
        this.visibleList.children.forEach((child, i) => {
            this._map.set(child.name as key, {
                toggle: child.getComponent(Toggle),
                disable: child.getComponent(Sprite),
                ratio: findComp(Label, child, 'Ratio'),
                title: findComp(Label, child, 'Title'),
                hidden: findComp(Label, child, 'Hidden'),
                type: types[i]
            });
        });
    }

    private _initToggleStates(): void {
        this.visibleList.children.forEach(child => {
            const { toggle } = this._map.get(child.name as key);
            const state = !!solar.getStateByName(child.name);
            toggle.setIsCheckedWithoutNotify(state);
            this.toggleVisibility(toggle, false);
        });
        this.scalar.progress = solar.magnifyRatio;
    }

    private _listen(): void {
        this.on(Transform.ZOOM_START, this.onVisitStart);
        this.on(Transform.VISIT_OVER, this.onVisitOver);
        this.on(Scale.RATIO_CHANGE, this.onRatioChanged);
        this.on(Panels.CLOSE, this.onPanelClosed);
        this.on(UI.VISIBLE, this.onVisibleChanged);
    }

    protected onVisitStart(): void {
        if (this.scaleGroup.activeInHierarchy) {
            this.blocker.enabled = true;
            this._twsScale.hideDelay = this.scalar.progress * REDUCING_SEC;
            this._twsScale.hide(true);
        }
        this._reducing = true; // 每次都要reducing, 因为此刻可能在星际范围
        if (this.introGroup.activeInHierarchy) this._twsIntro.hide(true);
        this._should_intro = false;
    }

    protected onVisitOver(): void {
        const { inSolar, inPlanetary, inSatellite, inStellar, centerName } = visit;
        if (inSolar) {
            this.blocker.enabled = false;
            this._twsScale.show(true);
        }
        if (inPlanetary || inSatellite) {
            const name = planetary.unbelt ? centerName : planetary.centerName;
            this._twsIntro.show(true);
            this.introName.string = 'celestial@' + name;
            this._should_intro = true;
        }
        if (inStellar) {
            this._twsIntro.show(true);
            this.introName.string = 'celestial@' + centerName;
            this._should_intro = true;
        }
    }

    protected onVisibleChanged(into_cn: key, shown: boolean, byPanel: boolean): void {
        if (byPanel) {
            const child = this.visibleList.getChildByName(into_cn);
            if (!!child) {
                const { toggle } = this._map.get(into_cn);
                toggle.setIsCheckedWithoutNotify(shown);
                this.toggleVisibility(toggle, false);
            }
        }
    }

    protected toggleVisibility(tgl: Toggle, notify: boolean = true): void {
        const key = tgl.node.name as key;
        const shown = tgl.isChecked;
        const { disable, ratio, title, hidden } = this._map.get(key);
        disable.enabled = !shown;
        title.color = shown ? LBL_UI_NAMES : LBL_GRAY;
        ratio.node.active = shown;
        hidden.node.active = !shown;
        const ntf = Solar.SUN.split('.').head + '.' + key;
        notify && this.notify(ntf, shown);
        notify && this.notify(VISIBLE, key, shown);
    }

    protected openIntroPanel(): void {
        this._twsIntro.hide(true);
        this.notify(Panels.OPEN, Res.Prefabs.INTRO_PANEL, 'bottom');
    }

    protected onPanelClosed(name: string, isFold: boolean): void {
        if (name === Res.Prefabs.INTRO_PANEL && isFold) {
            this._twsIntro.show(true);
        }
    }

    protected slideScalar(): void {
        const pct = this.scalar.progress;
        this.notify(Solar.RATIO, pct);
    }

    protected onRatioChanged(arg?: num.pct): void {
        const prefix = this._prefix[this._lang], actual = this._actual[this._lang];
        const pct = arg ?? this.scalar.progress;
        for (const [, value] of this._map) {
            value.ratio.string = pct < 0 ? actual : `${prefix} × ${Math.round(solar.getMagnified(value.type))}`;
        }
        this._scaleBar.fillRange = pct;
    }

    protected lateUpdate(): void {
        if (ui.language != this._lang) {
            this._lang = ui.language;
            this.onRatioChanged();
        }
    }

    private _scaleBar: Sprite = null;
    private _twsScale: TweenSwitcher;
    private _twsIntro: TweenSwitcher;
    private _should_intro: boolean = false;
    private _map: Map<key, value> = new Map;
    private _reducing: boolean = false;
    private _lang: string = null;
    private _prefix: i18nConfig;
    private _actual: i18nConfig;
}

declare type key = '太阳' | '行星' | '矮行星' | '小行星' | '彗星' | '小行星带';
declare type value = { disable: Sprite, toggle: Toggle, title: Label, hidden: Label, ratio: Label, type: into };
const { Solar, Panels, VISIBLE } = UI, { Transform, Scale } = View;
const types: into[] = ['star', 'planet', 'dwarf', 'minor', 'comet', 'belts'];
const REDUCING_SEC = 2;